﻿using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using System.Linq.Expressions;

namespace Sgr.EntityFrameworkCore.ValueConversion
{
    /// <summary>
    /// DateTime ⇄ DateTime（统一为 UTC）
    /// 未指定 Kind (Unspecified) 直接视为 UTC（不做时区换算，只补 Kind）
    /// Local -> ToUniversalTime()
    /// Utc   -> 原样
    /// 读取时数据库值（无 Kind）统一补为 Utc
    /// </summary>
    public sealed class UtcDateTimeDbConverter : ValueConverter<DateTime, DateTime>
    {
        public UtcDateTimeDbConverter(ConverterMappingHints? mappingHints = null)
            : base(ToProviderExpression(), FromProviderExpression(), mappingHints)
        {
        }

        private static Expression<Func<DateTime, DateTime>> ToProviderExpression()
            => v => v.Kind == DateTimeKind.Utc
                ? v
                : (v.Kind == DateTimeKind.Local
                    ? v.ToUniversalTime()
                    : DateTime.SpecifyKind(v, DateTimeKind.Utc)); // Unspecified 直接标记为 UTC

        private static Expression<Func<DateTime, DateTime>> FromProviderExpression()
            => v => DateTime.SpecifyKind(v, DateTimeKind.Utc);
    }

    /// <summary>
    /// DateTime ⇄ DateTime（统一为 UTC）
    /// 未指定 Kind (Unspecified) 先视为 Local，再转 UTC（适用于历史数据原本是本地时间但缺少 Kind）
    /// Local -> ToUniversalTime()
    /// Utc   -> 原样
    /// 读取时数据库值（无 Kind）统一补为 Utc
    /// </summary>
    public sealed class UtcDateTimeUnspecifiedAsLocalDbConverter : ValueConverter<DateTime, DateTime>
    {
        public UtcDateTimeUnspecifiedAsLocalDbConverter(ConverterMappingHints? mappingHints = null)
            : base(ToProviderExpression(), FromProviderExpression(), mappingHints)
        {
        }

        private static Expression<Func<DateTime, DateTime>> ToProviderExpression()
            => v => v.Kind == DateTimeKind.Utc
                ? v
                : (v.Kind == DateTimeKind.Local
                    ? v.ToUniversalTime()
                    : DateTime.SpecifyKind(v, DateTimeKind.Local).ToUniversalTime()); // Unspecified 视为 Local 再转 UTC

        private static Expression<Func<DateTime, DateTime>> FromProviderExpression()
            => v => DateTime.SpecifyKind(v, DateTimeKind.Utc);
    }
}